import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

import javax.annotation.Resource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.geo.Circle;
import org.springframework.data.geo.Distance;
import org.springframework.data.geo.GeoResults;
import org.springframework.data.geo.Point;
import org.springframework.data.redis.connection.RedisGeoCommands;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import com.github.pagehelper.PageInfo;


/**
 * 根据定位获取附近的人或地名,
 * 或保存信息
 */
@Component
@SuppressWarnings("rawtypes")
public class GeoUtils {

	@Resource(name="redisTemplateWrite")
    public RedisTemplate redisTemplateWrite;
    @Autowired
    public RedisTemplate redisTemplate;


    /**
     * 保存到redis geo的前缀键
     */
    private static final String GEO_KEY_PREFIX = "GEO:";


    /**
     * 用户分页后存入或查找时的前缀key。
     */
    private static final String GEO_CACHE_PREFIX = "GEO_CACHE:";


    @Autowired
    /**
     * 分页查询附近的人或地名
     * @return
     */
    public PageInfo nearByGeoDto2Page(){
        return null;
    }



    /**
     * 保存人或地名金纬度信息到redis。
     * @param key   redis KEY
     * @param point 经纬度信息
     * @param id  用户或地名的id
     * @return  受影响的行数
     */
    public Long save(String key, Point point, String id){
        return redisTemplateWrite.opsForGeo().add(GEO_KEY_PREFIX+key, new RedisGeoCommands.GeoLocation(id, point));
    }
    

    /**
     * 用户根据定位查找附近的人或地名     * @param point 定位的经纬度
     * @param key   redis保存的key
     * @param miles 多大范围内查找
     * @return  封装好的GeoDTO
     */
    public List<GeoVO> nearByGeoDto(Point point, String key, double miles){
        GeoResults<RedisGeoCommands.GeoLocation> radius  = near(point,key,miles);
        List<GeoVO> stageDTOS = new ArrayList<>();
        if (radius != null){
            radius.forEach(geoLocationGeoResult -> {
                RedisGeoCommands.GeoLocation<String> content = geoLocationGeoResult.getContent();
                //人或地名的id
                String id = content.getName();
                // 对应的经纬度坐标
                Point pos = content.getPoint();
                // 距离中心点的距离
                Distance dis = geoLocationGeoResult.getDistance();

                stageDTOS.add(new GeoVO(id,point.getX(),point.getY(),dis.getValue()));
            });
        }
        return stageDTOS;
    }


    /**
     * 用户根据定位查找附近的人或地名
     * @param point 定位的经纬度
     * @param key   redis保存的key
     * @param miles 多大范围内查找
     * @return  附近范围内的数据
     */
    public GeoResults<RedisGeoCommands.GeoLocation> near(Point point, String key, double miles){
        // 半径多少米范围内
        Distance distance = new Distance(miles , RedisGeoCommands.DistanceUnit.METERS);
        Circle circle = new Circle(point, distance);
        // 查看附近的几个人，这里设置查看5人
        // RedisGeoCommands.GeoRadiusCommandArgs geoRadiusCommandArgs = RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs().includeDistance().limit(5);
        // 如果无限制，去除.limit(5)
        RedisGeoCommands.GeoRadiusCommandArgs geoRadiusCommandArgs = RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs().includeDistance().includeCoordinates().sortAscending();
        GeoResults<RedisGeoCommands.GeoLocation> geoLocationGeoResult = redisTemplateWrite.opsForGeo().radius(GEO_KEY_PREFIX+key, circle, geoRadiusCommandArgs);
        return geoLocationGeoResult;
    }


    /**
     * 根据key删除缓存数据
     * @return  受影响的值
     */
    public Long remove(String key,String id){
       return redisTemplate.opsForGeo().remove(GEO_KEY_PREFIX+key,id);
    }


    /**
     * 分页获取周边
     * @param point 经纬度
     * @param key   redis键
     * @param miles 多大范围内查找
     * @param userid    用户的id
     * @param page  分页对象
     * @return  分页对象
     */
    public GeoPage<GeoVO> nearByPage(Point point, String key, double miles,String userid,GeoPage page){

        // 设置区间
        Integer startPage = (page.getPageIndex() - 1) * page.getPageSize();
        Integer endPage = page.getPageIndex() * page.getPageSize() - 1;

        //判断redis中指定的键是否存在
        Boolean aBoolean = redisTemplate.hasKey(GEO_CACHE_PREFIX+key+":"+userid);
        //不存在则去redis的geo数据里取
        if (!aBoolean){
            List<GeoVO> geoDTOS = nearByGeoDto(point, key, miles);
            // 把查出来的数据放到一个 redis 的list类型中，根据用户id来区分
            redisTemplate.opsForList().rightPushAll(GEO_CACHE_PREFIX+key+":"+userid,geoDTOS);
            //设置有效期，默认为10分钟
            Boolean expire = redisTemplate.expire(GEO_CACHE_PREFIX+key+":"+userid, 6000, TimeUnit.SECONDS);
        }
        // 从redis的list里获取指定区间的数据。
        List<GeoVO> list = redisTemplate.opsForList().range("GEO_CACHE:"+ key +":"+ userid, startPage,endPage);

        //创建分页对象
        GeoPage<GeoVO> geoPage = new GeoPage<>(page.getPageIndex(),page.getPageSize());
        geoPage.setList(list);
        return geoPage;
    }

}